package com.evil.kingdom.pojo.entity.action;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import com.evil.common.core.exception.BusinessException;
import com.evil.kingdom.pojo.entity.base.Belong;
import com.evil.common.mongo.entity.SingleName;
import com.evil.kingdom.pojo.entity.person.base.Person;
import com.evil.kingdom.pojo.entity.person.general.General;
import com.evil.kingdom.pojo.enums.result.RCodeEnum;
import com.evil.kingdom.utils.DijkstraUtil;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Set;

/**
 * 出征
 *
 * @author liyang
 * @date 2024-05-28 15:11
 */
@Getter
@Setter
@Slf4j
public class Warfare implements SingleName, Belong, Serializable {
    public static final Logger timerTaskLogger = LoggerFactory.getLogger("KINGDOM_TIMER_TASK_LOGGER");
    public static final Logger warfareLogger = LoggerFactory.getLogger("KINGDOM_WARFARE_LOGGER");

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 名称
     * (唯一)
     */
    @NotBlank(message = "出征名称不能为空")
    private String name;

    /**
     * 主帅名称
     */
    @NotBlank(message = "主帅名称不能为空")
    private String marshalName;

    /**
     * 归属（君主）
     *
     * @see Person#getName()
     */
    @NotBlank(message = "归属不能为空")
    private String belong;

    /**
     * 武将列表
     */
    @NotEmpty(message = "武将列表不能为空")
    private Set<General> generals;

    /**
     * 行军速度
     */
    @NotNull(message = "出征行军速度不能为空")
    private Double speed;

    /**
     * 出征路段名称
     */
    @NotBlank(message = "出征路段名称不能为空")
    private String warfareName;

    /**
     * 全路段
     */
    @NotEmpty(message = "出征路段不能为空")
    private LinkedList<RoutePosition> routes;

    /**
     * 当前所在路段
     * 城池与城池之间所有路线合并称呼路段
     */
    @NotNull(message = "出征当前所在路段不能为空")
    private Integer routeIndex;

    /**
     * 当前所在路段
     */
    @NotNull(message = "出征当前所在路段不能为空")
    private RoutePosition route;

    /**
     * 当前所在路线
     */
    @NotNull(message = "出征当前所在路线不能为空")
    private Integer lineIndex;

    /**
     * 当前所在路线
     */
    @NotNull(message = "出征当前所在路线不能为空")
    private ActionPosition line;

    /**
     * 当前所在位置
     */
    @NotNull(message = "出征当前所在位置不能为空")
    private AbstractPosition position;

    public Warfare(String marshalName, Set<General> generals, Double speed, @NotEmpty(message = "出征路段不能为空") LinkedList<RoutePosition> routes) {
        this.marshalName = marshalName;
        this.generals = generals;
        this.speed = speed;

        this.routes = routes;
        this.routeIndex = 0;
        this.route = this.routes.getFirst();
        this.lineIndex = 0;
        this.line = this.route.getPositions().getFirst();
        this.position = this.line.getDepart();
        this.name = IdUtil.fastSimpleUUID();
        this.warfareName = Vector.calVectorName(this.route.getDepartName(), this.routes.getLast().getDestName());
    }

    @Override
    public String getEntityName() {
        return "出征";
    }

    public void nextRoute(double travel) {
        if (this.routeIndex >= this.routes.size() + 1) {
            warfareLogger.error("warfare|nextRoute|error:routeIndex out of index");
            throw new BusinessException(RCodeEnum.RUN_ERROR);
        }
        String before = this.route.printInfo();
        this.routeIndex++;
        this.route = this.routes.get(this.routeIndex);
        this.lineIndex = 0;
        this.line = this.route.getPositions().getFirst();
        this.position = this.line.getDepart();
        this.travel(travel);
        warfareLogger.info("warfare|nextRoute|name:{},before:{},after:{}", this.name, before, this.route.printInfo());
    }

    public void nextLine(double travel) {
        if (this.lineIndex >= this.route.getPositions().size() + 1) {
            warfareLogger.error("warfare|nextRoute|error:lineIndex out of index");
            throw new BusinessException(RCodeEnum.RUN_ERROR);
        }
        String before = this.line.printInfo();
        this.lineIndex++;
        this.line = this.route.getPositions().get(this.lineIndex);
        this.position = this.line.getDepart();
        this.travel(travel);
        warfareLogger.debug("warfare|nextLine|name:{},before:{},after:{}", this.name, before, this.line.printInfo());
    }

    public void travel(double travel) {
        this.position = DijkstraUtil.offset(this.position, this.line.getDest(), travel);
    }

    public String printInfo() {
        return String.format("name:%s, route:%s, line:%s, position:%s", this.name, this.route.printInfo(), this.line.printInfo(), this.position.toString());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Warfare warfare = (Warfare) o;

        return name.equals(warfare.name);
    }

    /**
     * 判断两个行军是否相遇
     *
     * @param that     另一个行军
     * @param distance 相遇距离指数
     * @return 是否相遇
     */
    public boolean meet(Warfare that, double distance) {
        ArrayList<String> thisCityName = ListUtil.toList(this.route.getDepartName(), this.route.getDestName());
        ArrayList<String> thatCityName = ListUtil.toList(that.route.getDepartName(), that.route.getDestName());
        // 同路段
        if (thisCityName.containsAll(thatCityName)) {
            DijkstraUtil.Slope thisSlope = DijkstraUtil.calSlope(this.line.getDepart(), this.line.getDest());
            DijkstraUtil.Slope thatSlope = DijkstraUtil.calSlope(that.line.getDepart(), that.line.getDest());
            // 同路线（两个当前路线斜率相等，通过斜率判断，因为一个来的，一个回的）
            if (thisSlope.sameSlope(thatSlope)) {
                // 判断两点距离
                double v = DijkstraUtil.calWeight(this.position, that.position, this.speed);
                return v <= distance;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}
